/**
 * &copy; 2006 NewInstance.it
 */
package it.newinstance.jrainbow.parser.java;

import it.newinstance.jrainbow.parser.StateMachine;
import it.newinstance.jrainbow.source.TaggedChar;

/**
 * @author Luigi R. Viggiano
 * @version $Id: JavaStateMachine.java 151 2007-06-22 00:11:25Z luigi.viggiano $
 */
public enum JavaStateMachine implements StateMachine<JavaStateMachine> {
    
    CODE {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            
            if (chr.match("//")) {
                chr.getPrevious().setTag(JavaTag.COMMENT_LINE);
                chr.setTag(JavaTag.COMMENT_LINE);
                return COMMENT_LINE;                
            }
            
            if (chr.match("/**")) {
                chr.getPrevious().getPrevious().setTag(JavaTag.JAVADOC);
                chr.getPrevious().setTag(JavaTag.JAVADOC);
                chr.setTag(JavaTag.JAVADOC);
                return COMMENT_JAVADOC;
            }
            
            if (chr.match("/*") && chr.getNext().getChar() != '*') {
                chr.getPrevious().setTag(JavaTag.COMMENT_BLOCK);
                chr.setTag(JavaTag.COMMENT_BLOCK);
                return COMMENT_BLOCK;
            }
            
            // string handling
            if (chr.getChar() == '\"') {
                chr.setTag(JavaTag.STRING_CONSTANT);
                return STRING;
            }
            
            // char constant handling
            if (chr.getChar() == '\'') {
                chr.setTag(JavaTag.CHAR_CONSTANT);
                return CHAR;
            }
            
            // number constants handling
            if (Character.isDigit(chr.getChar()) && 
               (!Character.isJavaIdentifierPart(chr.getPrevious().getChar()))) {
                chr.setTag(JavaTag.NUMERIC_CONSTANT);
                return NUMBER;
            }
            
            // parenthesis handling 
            if ("()[]{}".indexOf(chr.getChar()) != -1) {
                chr.setTag(JavaTag.PARENTHESIS);
                return CODE;
            }
            
            // annotation handling
            if (chr.getChar() == '@') {
                chr.setTag(JavaTag.JAVA_ANNOTATION);
                return ANNOTATION;
            }
            
            // other code
            chr.setTag(JavaTag.CODE);
            return CODE;
        }  
    },

    COMMENT_LINE {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            if (chr.getChar() == '\n' || chr.getChar() == '\r') {
                chr.setTag(JavaTag.CODE);
                return CODE;
            }
            chr.setTag(JavaTag.COMMENT_LINE);
            return COMMENT_LINE;
        }  
    }, 
        
    COMMENT_BLOCK {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            chr.setTag(JavaTag.COMMENT_BLOCK);
            if (chr.match("*/")) 
                return CODE;
            return COMMENT_BLOCK;
        }
    }, 
    
    COMMENT_JAVADOC {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            chr.setTag(JavaTag.JAVADOC);
            if (chr.match("*/")) 
                return CODE;
            if (chr.getChar() == '@' 
                && !Character.isJavaIdentifierPart(chr.getPrevious().getChar())
                && Character.isJavaIdentifierPart(chr.getNext().getChar())) {
                chr.setTag(JavaTag.JAVADOC_KEYWORD);
                return JAVADOC_KEYWORD;
            }
            return COMMENT_JAVADOC;
        }
    },
    
    JAVADOC_KEYWORD {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            if (!Character.isJavaIdentifierPart(chr.getChar())) {
                chr.setTag(JavaTag.JAVADOC);
                return COMMENT_JAVADOC;
            } 
            chr.setTag(JavaTag.JAVADOC_KEYWORD);
            return JAVADOC_KEYWORD;
        }        
    },
    
    STRING {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            chr.setTag(JavaTag.STRING_CONSTANT);
            if (chr.getChar() == '\\')
                return ESCAPING_STRING;
            if (chr.getChar() == '\"') 
                return CODE;
            
            return STRING;
        }
    }, 
    
    ESCAPING_STRING {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            chr.setTag(JavaTag.STRING_CONSTANT);            
            return STRING;
        }
    },
    
    CHAR {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            chr.setTag(JavaTag.CHAR_CONSTANT);
            if (chr.getChar() == '\\') 
                return ESCAPING_CHAR;
            if (chr.getChar() == '\'') 
                return CODE;
            return CHAR;
        }        
    },
    
    ESCAPING_CHAR {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {
            chr.setTag(JavaTag.CHAR_CONSTANT);            
            return CHAR;
        }
    },

    NUMBER {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {            
            if (Character.isDigit(chr.getChar()) || 
                    "dDlLxXfF.".indexOf(chr.getChar()) != -1) {
                chr.setTag(JavaTag.NUMERIC_CONSTANT);
                return NUMBER;
            } 
            return CODE.handle(chr);
        }                
    },

    ANNOTATION {
        @Override
        public JavaStateMachine handle(TaggedChar chr) {            
            if (Character.isJavaIdentifierPart(chr.getChar())) {
                chr.setTag(JavaTag.JAVA_ANNOTATION);
                return ANNOTATION;
            }                 
            return CODE.handle(chr);
        }                
    },
    
    ;

    public abstract JavaStateMachine handle(TaggedChar chr);
}
